package cn.shop.gateway.http;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.server.HttpServerRequest;
import cn.hutool.http.server.HttpServerResponse;
import cn.hutool.http.server.SimpleServer;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.shop.gateway.Config;
import cn.shop.gateway.db.DbUtil;
import cn.shop.gateway.model.*;
import cn.shop.gateway.utils.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static cn.shop.gateway.utils.Context.*;
import static cn.shop.gateway.utils.Context.Path.*;
import static cn.shop.gateway.utils.Context.orderSellFilePath;

/**
 * @author : 李一涵
 * @date : Created in 2021-12-14 14:51
 * @description :
 */
public class WebServer {

    private static long MAX;
    private static long STEP;
    private static long MIN;


    private final static Log LOG = LogFactory.get(WebServer.class);

    public static void start() {
        MAX = Config.getLong(Context.MAX);
        STEP = Config.getLong(Context.STEP);
        MIN = Config.getLong(Context.MIN);
        createServer().start();
    }

    private static SimpleServer createServer() {
        return HttpUtil.createServer(Config.getInt(PORT))
                .addAction(PARTITION_ADD, WebServer::addGamePartition)
                .addAction(PARTITION_REMOVE, WebServer::removeGamePartition)
                .addAction(BUY_ORDER, WebServer::buyOrder)
                .addAction(SET_URL, WebServer::setUrl)
                .addAction(CHECK_CODE, WebServer::getCode)
                .addAction(BIND, WebServer::bindSuccess)
                .addAction(UNBIND, WebServer::unbind)
                .addAction(SECRET, WebServer::updateSecret)
                .addAction(PING, WebServer::ping);
    }

    private static void ping(HttpServerRequest request, HttpServerResponse response) {
        response.write(ResponseInfo.ok().toString());
    }

    private static void addGamePartition(HttpServerRequest request, HttpServerResponse response) {
        if (checkSecret(request)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        String body = request.getBody();
        if (StrUtil.isBlank(body)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        MerchantGamePartition partition;
        try {
            partition = JsonUtil.mapper().readValue(body, MerchantGamePartition.class);
        } catch (Exception e) {
            LOG.error("添加分区出错", e);
            response.write(ResponseInfo.fail("添加分区出错").toString());
            return;
        }
        if (partition == null) {
            response.write(ResponseInfo.fail("分区信息为空").toString());
            return;
        }
        List<GameNpc> npcs = partition.getNpcs();
        if (CollectionUtil.isEmpty(npcs)) {
            response.write(ResponseInfo.fail("npc列表为空").toString());
            return;
        }
        int count;
        try {
            count = DbUtil.putGamePartition(partition);
        } catch (Exception e) {
            response.write(ResponseInfo.fail("保存分区信息失败").toString());
            return;
        }
        if (count <= 0) {
            response.write(ResponseInfo.fail("保存分区信息失败").toString());
            return;
        }
        Entity template = DbUtil.getGameTemplateById();
        if (template == null) {
            response.write(ResponseInfo.fail("模板信息不存在").toString());
            return;
        }
        String currency = template.getStr(MerchantGameTemplate.Field.CURRENCY);
        if (!currency.contains("|")) {
            response.write(ResponseInfo.fail("模板信息错误").toString());
            return;
        }
        String[] temp = currency.split("\\|");
        String name = temp[0], code = temp[1];
        double ratio = template.getDouble(MerchantGameTemplate.Field.RATIO);
        if (partition.getCreateScript()) {
            List<String> npcList = FileUtil.readLines(npcLocationFilePath(partition.getServerFolder()), CharsetUtil.CHARSET_GBK);
            String npcName = npcs.get(0).getName();
            npcList = npcList.stream().filter(s -> !s.contains(npcName)).collect(Collectors.toList());
            npcs.stream()
                    .map(n -> n.getName() + "\t" + n.getMapCode() + "\t" + n.getPositionX() + "\t" + n.getPositionY() + "\t" + n.getName() + "\t1\t" + n.getStyleCode() + "\t0\t0\t0")
                    .forEach(npcList::add);
            if (!npcList.isEmpty()) {
                FileUtil.writeLines(npcList, npcLocationFilePath(partition.getServerFolder()), CharsetUtil.CHARSET_GBK);
            }
        }
        if (!initDic(partition, name, code)) {
            response.write(ResponseInfo.fail("初始化脚本出错").toString());
            return;
        }
        for (GameNpc npc : npcs) {
            File file = new File(npcScriptFilePath(partition.getServerFolder(), npc.getName(), npc.getMapCode()));
            if (!file.exists()) {
                if (!createFile(file)) {
                    LOG.warn("创建Npc事件脚本失败");
                    response.write(ResponseInfo.fail("创建脚本"+file.getAbsolutePath()+"失败").toString());
                    return;
                }
            }
            if (partition.getCreateScript()) {
                String url = "http://{}.haojy.com";
                ScriptUtil.createSellScript(file, name, code, (int) ratio, MAX, partition.getId(), StrUtil.format(url, partition.getAlias()));
            }
            LOG.info("脚本内容写入完成");
        }
        WatchUtil.watch(orderSellFilePath(partition.getServerFolder()));
        response.write(ResponseInfo.ok().toString());
    }

    private static void removeGamePartition(HttpServerRequest request, HttpServerResponse response) {
        if (checkSecret(request)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        String body = request.getBody();
        if (StrUtil.isBlank(body)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        LOG.info("删除分区{}", body);
        Long[] ids = Arrays.stream(body.split(","))
                .map(Long::valueOf)
                .collect(Collectors.toList())
                .toArray(new Long[]{});
        List<Entity> list = DbUtil.removeGamePartition(ids);
        if (CollectionUtil.isNotEmpty(list)) {
            for (Entity entity : list) {
                WatchUtil.stopWatch(orderSellFilePath(entity.getStr(MerchantGamePartition.Field.FOLDER)));
            }
        }
        response.write(ResponseInfo.ok("").toString());
    }

    private static void buyOrder(HttpServerRequest request, HttpServerResponse response) {
        if (checkSecret(request)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        String body = request.getBody();
        if (StrUtil.isBlank(body)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        List<BuyOrder> orders;
        try {
            orders = JsonUtil.mapper().readValue(body, new TypeReference<List<BuyOrder>>() {});
        } catch (Exception e) {
            LOG.error("接收订单出错", e);
            response.write(ResponseInfo.fail("接收订单出错").toString());
            return;
        }
        for (BuyOrder order : orders) {
            consumeOrder(order);
        }
        response.write(ResponseInfo.ok().toString());
    }

    private static void setUrl(HttpServerRequest request, HttpServerResponse response) {
        if (checkSecret(request)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        String url = request.getParam("url");
        String port = request.getParam("port");
        if (url != null && StrUtil.isNumeric(port)) {
            ApiUtil.init(url, Integer.parseInt(port));
            response.write(ResponseInfo.ok().toString());
        } else {
            response.write(ResponseInfo.fail("参数错误").toString());
        }
    }

    private static void getCode(HttpServerRequest request, HttpServerResponse response) {
        if (checkSecret(request)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        String body = request.getBody();
        if (StrUtil.isBlank(body)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        CodeQuery query;
        try {
            query = JsonUtil.mapper().readValue(body, CodeQuery.class);
        } catch (Exception e) {
            response.write(ResponseInfo.fail("查询验证码出错").toString());
            return;
        }
        Entity entity = DbUtil.getGamePartitionById(query.getPartitionId());
        if (entity == null) {
            response.write(ResponseInfo.fail("游戏分区不存在").toString());
            return;
        }
        String folder = entity.getStr(MerchantGamePartition.Field.FOLDER);
        String path = playerCodeFilePath(folder);
        List<String> list = FileUtil.readLines(path, CharsetUtil.CHARSET_GBK);
        boolean hasCode = list.stream().anyMatch(s -> s.equals(query.getAccount() + "|" + query.getNickname() + "|" + query.getCode()));
        path = playerBindFilePath(folder);
        list = FileUtil.readLines(path, CharsetUtil.CHARSET_GBK);
        boolean noBind = list.stream().noneMatch(s -> s.equals(query.getAccount() + "|" + query.getNickname()));
        try {
            response.write(JsonUtil.mapper().writeValueAsString(ResponseInfo.ok(hasCode && noBind)));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            response.write(ResponseInfo.fail("系统错误").toString());
        }
    }

    private static void bindSuccess(HttpServerRequest request, HttpServerResponse response) {
        if (checkSecret(request)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        String body = request.getBody();
        if (StrUtil.isBlank(body)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        CodeQuery query;
        try {
            query = JsonUtil.mapper().readValue(body, CodeQuery.class);
        } catch (Exception e) {
            response.write(ResponseInfo.fail("查询验证码出错").toString());
            return;
        }
        Entity entity = DbUtil.getGamePartitionById(query.getPartitionId());
        if (entity == null) {
            response.write(ResponseInfo.fail("游戏分区不存在").toString());
            return;
        }
        String folder = entity.getStr(MerchantGamePartition.Field.FOLDER);
        String path = playerBindFilePath(folder);
        FileUtil.appendString(query.getAccount()+"|"+query.getNickname(), path, CharsetUtil.CHARSET_GBK);
        response.write(ResponseInfo.ok().toString());
    }

    private static void unbind(HttpServerRequest request, HttpServerResponse response) {
        if (checkSecret(request)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        String body = request.getBody();
        if (StrUtil.isBlank(body)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        CodeQuery query;
        try {
            query = JsonUtil.mapper().readValue(body, CodeQuery.class);
        } catch (Exception e) {
            response.write(ResponseInfo.fail("查询验证码出错").toString());
            return;
        }
        Entity entity = DbUtil.getGamePartitionById(query.getPartitionId());
        if (entity == null) {
            response.write(ResponseInfo.fail("游戏分区不存在").toString());
            return;
        }
        String folder = entity.getStr(MerchantGamePartition.Field.FOLDER);
        String path = playerBindFilePath(folder);
        List<String> list = FileUtil.readLines(path, CharsetUtil.CHARSET_GBK);
        FileUtil.writeString("", path, CharsetUtil.CHARSET_GBK);
        list = list.stream().filter(s -> !s.equals(query.getAccount()+"|"+query.getNickname())).collect(Collectors.toList());
        FileUtil.appendLines(list, path, CharsetUtil.CHARSET_GBK);
        response.write(ResponseInfo.ok().toString());
    }

    private static void updateSecret(HttpServerRequest request, HttpServerResponse response) {
        if (checkSecret(request)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        String body = request.getBody();
        if (StrUtil.isBlank(body)) {
            response.write(ResponseInfo.fail("参数错误").toString());
            return;
        }
        DbUtil.saveSecret(body);
        response.setContentType("application/json;charset=UTF-8");
        response.write(ResponseInfo.ok().toString());
    }

    private static boolean checkSecret(HttpServerRequest request) {
        String secret = request.getHeader("secret");
        if (StrUtil.isBlank(secret)) {
            LOG.info("收到的secret为空");
            return true;
        }
        String s = DbUtil.getSecret();
        LOG.info("收到的secret：{}, 查出的secret：{}", secret, s);
        return !StrUtil.equals(secret, s);
    }

    private static boolean createFile(File file) {
        boolean success = false;
        try {
            success = file.createNewFile();
        } catch (IOException e) {
            LOG.warn("创建脚本{}失败", file, e);
        }
        return success;
    }

    private static void consumeOrder(BuyOrder order) {
        Entity entity = DbUtil.getGamePartitionById(order.getPartitionId());
        Entity template = DbUtil.getGameTemplateById();
        Map<Long, Integer> map = cal(order.getNum());
        for (Map.Entry<Long, Integer> entry : map.entrySet()) {
            writeOrder(template.getStr(MerchantGameTemplate.Field.CURRENCY), entry.getKey(), entry.getValue(), entity.getStr(MerchantGamePartition.Field.FOLDER), order.getBuyAccount());
        }
        DbUtil.saveOrderCheck(order.getId());
    }

    private static void writeOrder(String name, long digit, long count, String serverFolder, String account) {
        List<String> lines = new ArrayList<>();
        for (long i = 0; i < count; i++) {
            lines.add(account);
        }
        if (digit > MAX) {
            count = count * (digit / MAX);
            digit = MAX;
        }
        if (digit == 0) {
            return;
        }
        String[] temp = name.split("\\|");
        String path = orderBuyFilePath(serverFolder, temp[0], String.valueOf(digit));
        File file = new File(path);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                LOG.warn("接收订单出错,道具 {} 面额 {}, 数量 {}, 账号 {}, 分区目录 {}", temp[0], digit, count, account, serverFolder, e);
                return;
            }
        }
        try {
            FileUtil.appendLines(lines, file, CharsetUtil.CHARSET_GBK);
        } catch (Exception e) {
            LOG.warn("接收订单出错,道具 {} 面额 {}, 数量 {}, 账号 {}, 分区目录 {}", temp[0], digit, count, account, serverFolder, e);
        }

    }

    private static boolean initDic(MerchantGamePartition partition, String name, String code) {
        String folder = partition.getServerFolder();
        File fd = new File(scriptPath(folder));
        if (!fd.exists()) {
            fd.mkdirs();
        }
        LOG.info("创建脚本文件夹结束 {}", fd.getPath());
        boolean create = true;
        fd = new File(orderSellFilePath(folder));
        if (!fd.exists()) {
            if (!createFile(fd)) {
                return false;
            }
        }
        LOG.info("创建商品文件结束 {}", fd.getPath());
        fd = new File(orderSellFilePathBak(folder));
        if (!fd.exists()) {
            if (!createFile(fd)) {
                return false;
            }
        }
        LOG.info("创建商品备份文件结束 {}", fd.getPath());
        fd = new File(playerCodeFilePath(folder));
        if (!fd.exists()) {
            if (!createFile(fd)) {
                return false;
            }
        }
        LOG.info("创建验证文件结束 {}", fd.getPath());
        fd = new File(playerBindFilePath(folder));
        if (!fd.exists()) {
            if (!createFile(fd)) {
                return false;
            }
        }
        LOG.info("创建绑定文件结束 {}", fd.getPath());
        fd = new File(orderBuyPath(folder, name));
        if (!fd.exists()) {
            if (!fd.mkdirs()){
                return false;
            }
        }
        LOG.info("创建领取文件夹结束 {}", fd.getPath());
        fd = new File(orderBuyReceiveFile(folder, name));
        if (!fd.exists()) {
            create = createFile(fd);
        }
        LOG.info("创建领取文件结束 {}", fd.getPath());
        ScriptUtil.createBuyScript(fd, name, code, MAX, MIN, STEP);
        return create;
    }

    private static Map<Long, Integer> cal(long num) {
        Map<Long, Integer> map = new LinkedHashMap<>(10);
        String s = String.valueOf(num);
        int len = s.length();
        for (int i = 0; i < len; i++) {
            if (i == len - 1) {
                map.put((long)(s.charAt(i) - '0'), 1);
            } else {
                if (s.charAt(i) != '0') {
                    map.put((long)Math.pow(10, len - i - 1) * (s.charAt(i) - '0'), 1);
                }
            }
        }
        return map;
    }
}
